// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.Build.Framework;

namespace Microsoft.NET.Build.Tasks
{
    public sealed class GenerateGlobalUsings : TaskBase
    {
        [Required]
        public ITaskItem[] Usings { get; set; }

        [Output]
        public string[] Lines { get; set; }

        protected override void ExecuteCore()
        {
            if (Usings.Length == 0)
            {
                Lines = Array.Empty<string>();
                return;
            }

            var usings = Usings.Select(UsingInfo.Read)
                .OrderBy(static k => k, UsingInfoComparer.Instance)
                .Distinct(UsingInfoComparer.Instance);

            var lines = new string[Usings.Length + 1];
            lines[0] = "// <auto-generated/>";

            var index = 1;
            var lineBuilder = new StringBuilder();
            foreach (var @using in usings)
            {
                lineBuilder.Clear();
                lineBuilder.Append("global using ");

                if (@using.Static)
                {
                    lineBuilder.Append("static ");
                }

                if (!string.IsNullOrEmpty(@using.Alias))
                {
                    lineBuilder.Append(@using.Alias)
                        .Append(" = ");
                }

                lineBuilder.Append(@using.Namespace)
                    .Append(';');

                lines[index++] = lineBuilder.ToString();
            }

            Lines = lines;
        }

        private readonly struct UsingInfo
        {
            public static UsingInfo Read(ITaskItem taskItem)
            {
                return new UsingInfo(
                    taskItem.ItemSpec,
                    taskItem.GetBooleanMetadata("Static") == true,
                    taskItem.GetMetadata("Alias"));
            }

            private UsingInfo(string @namespace, bool @static, string alias)
            {
                Namespace = @namespace;
                Static = @static;
                Alias = alias;
            }

            public string Namespace { get; }
            public bool Static { get; }
            public string Alias { get; }
        }

        private sealed class UsingInfoComparer : IComparer<UsingInfo>, IEqualityComparer<UsingInfo>
        {
            public static readonly UsingInfoComparer Instance = new();

            public int Compare(UsingInfo x, UsingInfo y)
            {
                var @static = x.Static.CompareTo(y.Static);
                if (@static != 0)
                {
                    return @static;
                }

                var alias = x.Alias.CompareTo(y.Alias);
                if (alias != 0)
                {
                    return alias;
                }

                return StringComparer.Ordinal.Compare(x.Namespace, y.Namespace);
            }

            public bool Equals(UsingInfo x, UsingInfo y)
            {
                return Compare(x, y) == 0;
            }

            public int GetHashCode(UsingInfo obj)
            {
                return StringComparer.Ordinal.GetHashCode(obj.Namespace);
            }
        }
    }
}
